iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 22
0
自我挑戰組

Android Architecture 及 Unit Test系列 第 22

[Day 22] Test:Part 4 UseCase and ViewModel

  • 分享至 

  • xImage
  •  

前幾天也有介紹過,透過 UseCase 將資料交換的相關邏輯抽出來,既可以讓 ViewModel 變得更簡潔,同時一個個的 UseCase 也能夠直接進行測試,這樣處理 UseCase 及 ViewModel 的測試就不會太複雜了,下面開始進行今天的主題。

UseCase

因為邏輯拆分到 UseCase 後已經變得很簡單了,所以測試起來沒有特別困難的地方,我對 UseCase 的測試思路是塞入各種不同的資料來檢查最後回傳的 Result 結果是否正確。

GetTasksUseCase 為例,可以透過輸入各種不同的 filter type 來檢查 Result 的結果。或是看看沒有資料時的狀態,或者幹脆看看程式碼 crash 時有沒有正確處理。

這邊沒有太多值得細講的內容,所以直接在這裡附上 完成的程式碼 供大家參考。

ViewModel

在進行 ViewModel 的測試時幾乎與前面差不多,除了會遇到一個情況:怎麼測試 LiveData ?

在聊這個問題之前,需要知道一件事情:

在使用 LiveData 時常常會搭配 LiveData 的 Transformations API 進行一些操作,方便做一些資料的變換或是一些處理等。但這些使用了 Transformations.map() 等方法的 LiveData 必須要被 觀察 (observe) 才能得到資料的變化。

因此在實務操作時:

除了 MutableLiveData 之外,很多時候使用 LiveData 都需要 observe

MutableLiveData 除外的原因是因為可以直接使用 MutableLiveData.value 獲得 LiveData 的資料,但這麼做一旦哪天在程式碼裡把 MutableLiveData 改成其他類型的 LiveData 時很有可能測試就壞掉了,所以保險起見我通常會建議別人測試 LiveData 還是乖乖 observe 比較好。

關於這個問題 Google issue tracker 也有人反應了,但不知道何時才會有一個官方的解決方案。不過我們可以自己寫一個簡單的 Util 來處理這個問題:

import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException

fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeout: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(t: T) {
            data = t
            latch.countDown()
            this@getOrAwaitValue.removeObserver(this)
        }
    }
    this.observeForever(observer)

    afterObserve.invoke()

    if (!latch.await(time, timeout)) {
        this.removeObserver(observer)
        throw TimeoutException("LiveData value was never set")
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}

......

@Test
fun testSample() {
    ......
    assertEquals(viewModel.liveData1.getOrAwaitValue(), "foo")
}

具體的思路是寫一個 observeForever 方法,在 observe 後移除掉 observer ,並且為了避免程式無回應,在等待一段時間後會強行停止。

當然使用時也要記得加上 InstantTaskExecutorRule 以確保不會有線程上的問題。

解決了 LiveData 的問題後,TasksViewModel 的測試就可以繼續進行了,最後附上完成的 程式碼 給大家參考。


上一篇
[Day 21] Test:Part 3 Repository
下一篇
[Day 23] Test:Part 5 UI Test
系列文
Android Architecture 及 Unit Test30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言